package com.example.demo.utils;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 使用组合的方式可能会更加优雅些，
 * 但是由于没有实现 Map 接口，
 * 所以就不能使用 Collections.synchronizedMap() 方式来保证线程安全性了，
 * 所以需要在每个方法处增加 synchronized 来确保线程安全。实现方式如下：
 *
 * @ClassName LRUCache
 * @Author lys4134
 * @Date 2021/08/03 13:59:53
 * @Description LRUCache
 * @Version 1.0
 **/
public class LRUCache<K, V> {
    private int capacity;
    private Map<K, V> cacheMap;

    /**
     * 组合的方式也显得非常简单，有两点需要注意：
     * <p>
     * 保证每个方法的线程安全
     * put 时，需要查看当前容量是否超过设置的容量，
     * 超过则需要删除第一个元素。
     * 当然小编这种是实现方式不是很优雅，
     * 这么做知识为了能够更加好阐述 LRU 的实现。
     * 更好的方案是在构造 LinkedHashMap 时，重写 removeEldestEntry()，如下：
     *
     * @param capacity
     */
    public LRUCache(int capacity) {

        this.capacity = capacity;

        cacheMap = new LinkedHashMap<K, V>(capacity, 075f, true) {
            @Override
            public boolean removeEldestEntry(Map.Entry eldest) {
                return size() > capacity;
            }
        };
    }

    public synchronized void put(K k, V v) {
        cacheMap.put(k, v);
        // 移除第一个元素
//        if(cacheMap.size() > capacity){
//            K first = this.keyIterator().next();
//            cacheMap.remove(first);
//        }
    }

    public synchronized V get(K k) {
        return cacheMap.get(k);
    }

    public Iterator<K> keyIterator() {
        return cacheMap.keySet().iterator();
    }


    public static void main(String[] args) {
        LRUCache lruCache = new LRUCache(5);
        lruCache.put("1", "a");
        lruCache.put("2", "b");
        lruCache.put("3", "c");
        lruCache.put("4", "d");
        lruCache.put("5", "e");
        System.out.println("插入 5 个元素后的顺序");
        println(lruCache);
        // 插入第 6 个元素
        lruCache.put("6", "e");
        System.out.println("插入 第 6 个元素后的顺序");
        println(lruCache);
        // 访问 第 3 个元素
        lruCache.get("3");
        System.out.println("访问元素 3 后的顺序");
        println(lruCache);
    }

    private static void println(LRUCache lruCache) {
        for (Iterator it = lruCache.keyIterator(); it.hasNext(); ) {
            System.out.print(it.next() + "    ");
        }
        System.out.println();
    }
}
